#include "myhead.h"

const string filePath = "./all_num.txt";

#define WINDOW_NAME "Courier_SmartLink"

// 读取文件中的订单信息
void readOrderInfoFromFile(vector<string>& orders)
{
    ifstream file(filePath);
    string line;
    if (file.is_open())
    {
        while (getline(file, line))
        {
            orders.push_back(line);
        }
        file.close();
    }else
    {
        cerr << "Error opening file!" << endl;
    }
}

// 读取现有库存(数字后缀是IN#的表示还在库中)，并添加到我的动态数组中
void read_existing_inventory(vector<string>& infoArr)
{
    // 创建一个动态数组来存入读取文件中的所有订单信息
    vector<string> orders;
    readOrderInfoFromFile(orders);

    // 使用迭代器来遍历和修改 infoArr（避免因删除而导致迭代器失效）
    for (auto it = orders.begin(); it != orders.end(); ++it)
    {
        const string& order = *it;

        // 如果订单状态为 "IN#"，则添加到动态数组中
        if (order.find("IN#") != string::npos)
        {
            // 去掉 "IN#" 后缀插入
            infoArr.push_back(order.substr(0, order.find("IN#")));
        }

        // 如果订单状态为 "OUT#"，则删除该订单
        if (order.find("OUT#") != string::npos)
        {
            // 提取出基本的订单号（去掉 "OUT#" 后缀）
            string baseOrderNumber = order.substr(0, order.find("OUT#"));

            // 在 infoArr 中查找并删除对应的订单
            bool found = false;
            for (auto itInfo = infoArr.begin(); itInfo != infoArr.end(); ++itInfo)
            {
                // 检查每个订单是否是对应的订单
                if (itInfo->find(baseOrderNumber) != string::npos)
                {
                    // 找到对应的订单，删除它
                    infoArr.erase(itInfo);
                    found = true;
                    break; // 一旦找到并删除，跳出循环
                }
            }

            // 如果找不到对应的 "IN#" 订单，可以记录错误或做其他处理
            if (!found)
            {
                // 这里可以输出日志，说明找不到对应的 "IN#" 订单
                cout << "Unable to find the corresponding IN # order: " << baseOrderNumber << endl;
            }
        }
    }
}

// 查找订单状态
void inquireOrderStatus(const string& inputText)
{
    // 读取文件中的所有订单信息
    vector<string> orders;
    readOrderInfoFromFile(orders);

    bool found = false;
    bool hasInStatus = false;
    bool hasOutStatus = false;

    // 遍历文件中的订单信息，查找匹配的订单号
    for (const auto& order : orders)
    {
        if (order.find(inputText) != string::npos)
        {
            found = true;

            // 判断订单的状态
            if (order.find("IN#") != string::npos)
            {
                hasInStatus = true; // 有 IN# 状态
            }
            else if (order.find("OUT#") != string::npos)
            {
                hasOutStatus = true; // 有 OUT# 状态
            }
        }
    }

    // 根据订单状态来输出结果
    if (found)
    {
        if (hasOutStatus)
        {
            // 如果有 OUT# 状态，说明订单已经出库
            cout << "Order number " << inputText << " has been shipped out." << endl;
        }
        else if (hasInStatus)
        {
            // 如果只有 IN# 状态，说明订单仍在库中
            cout << "Order number " << inputText << " is in the inventory." << endl;
        }
        else
        {
            // 如果没有找到 IN# 或 OUT#，可能是一个无效的状态
            cout << "Order number " << inputText << " has no valid status." << endl;
        }
    }
    else
    {
        // 如果没有找到匹配的订单
        cout << "No such order number found or it has already been shipped out." << endl;
    }
}

int main()
{
    cv::Mat frame = cv::Mat(800, 1280, CV_8UC3);//高度，宽度，类型

    // 初始化cui并告诉它创建一个OpenCV窗口，即cv::namedWindow（WINDOW_NAME）.
    cvui::init(WINDOW_NAME);

    // 打开摄像头
    cv::VideoCapture cap(0);
//  int deviceID = 0; // 0 = open default camera  0 默认 摄像头
    int apiID = cv::CAP_ANY; // 0 = autodetect default API  0 自动 监测  摄像头采集的 格式
    if (!cap.isOpened())
    {
        cerr << "ERROR! Unable to open camera" << endl;
        return -1;
    }

    cout << "Start grabbing" << endl<< "Press any key to terminate" << endl;

    // QR码检测器
    cv::QRCodeDetector qrDecoder;

    // 设置输入框:创建一个字符串变量来存储输入框的内容
    string inputText = "";


    cv::Mat out = cv::imread("./icon/out.png", cv::IMREAD_COLOR);
    cv::Mat down = cv::imread("./icon/down.png", cv::IMREAD_COLOR);
    cv::Mat over = cv::imread("./icon/over.png", cv::IMREAD_COLOR);

    //FILE * all_num = fopen(filePath, "a+");《《原来的方法

    vector<string> infoArr; // 定义一个元素类型为int的动态数组

    read_existing_inventory(infoArr);// 将文件里面含有IN#的订单号全部先展示出来

    int inventory_quantity = 0;//入库量

    int pick_up_vol = 0;//出库量

    //出入库状态切换按钮
    bool IN_status;

    bool OUT_status;

    //默认状态
    bool NO_status = true;

    //为了降低检测频率不让摄像头一直处于检测状态
    int frameCount = 0;  // 帧计数器

    int detectInterval = 10;  // 每10帧检测一次

    while (true)
    {
        // 用一种漂亮的颜色填充框架
        frame = cv::Scalar(49, 52, 49);

        //显示库中所有单号
        cvui::window(frame, 40, 560, 400, 220, "Inventory tracking number");
        int text_X = 50;
        int text_Y = 580;

        // 遍历订单号数组并显示 库中数字含有后缀时打印在库订单的方法
        /*for (std::vector<string>::size_type i = 0; i < infoArr.size(); i++)
        {
            string orderNumber = infoArr[i];

            // 去除 IN# 后缀，只保留数字部分
            if (orderNumber.find("IN#") != string::npos)
            {
                orderNumber = orderNumber.substr(0, orderNumber.find("IN#"));
            }

            // 为对齐，重新初始化一次Y轴值
            if (text_Y >= 700)
            {
                text_Y = 580;  // 如果文本超过画面高度，重置Y轴值
            }else
            {
                if (i <= 6)
                {
                    cvui::text(frame, text_X, text_Y += 20, orderNumber, 0.5);
                }else
                {
                    cvui::text(frame, text_X + 50, text_Y += 20, orderNumber, 0.5);
                }
            }
        }*/


        // 遍历订单号数组并显示 库中数字无后缀时打印在库订单的方法
        for (std::vector<int>::size_type i = 0; i < infoArr.size(); i++)
        {
            //为对齐，这里重新初始化一次Y轴值
            if(text_Y >= 740)
            {
                text_Y = 580;
            }

            if(i < 8)
            {
                cvui::text(frame, text_X, text_Y += 20, infoArr[i], 0.5);
            }
            else
            {
                cvui::text(frame, text_X + 50, text_Y += 20, infoArr[i], 0.5);
            }
        }

        // 创建摄像头框架
        cv::Mat frameCam;

        cap >> frameCam; // 获得摄像头数据 存入frameCam

        if (frameCam.empty())
        {
            break;
        }

        cvui::window(frame, 720, 40, 200, 200, "Total inventory");//Total inventory总库存
        cvui::text(frame, 780, 110,to_string(infoArr.size()), 3.5);//库存总数infoArr.size();

        cvui::window(frame, 940, 40, 200, 200, "Today's inventory quantity");//Today's inventory quantity今日入库量
        cvui::text(frame, 1000, 110, to_string(inventory_quantity), 3.5);

        cvui::window(frame, 720, 260, 200, 200, "Express delivery delay");//Express delivery delay快递滞留
        cvui::text(frame, 780, 330, to_string(infoArr.size()), 3.5);

        cvui::window(frame, 940, 260, 200, 200, "Today's pick-up volume");//Today's pick-up volume今日取件量
        cvui::text(frame, 1000, 330, to_string(pick_up_vol), 3.5);


        // 解码二维码
        std::string decodedInfo;
        std::vector<cv::Point> points;
        cv::Mat straight_qrcode;


/*// 函数接口分析以及参数说明

       std::string cv::QRCodeDetector::detectAndDecode(
           InputArray img,
           OutputArray points = noArray(),
           OutputArray straight_qrcode = noArray()
       )
       参数说明：
       img：输入图像，类型为 InputArray。这个图像应该包含一个二维码，可以是灰度图像或彩色图像。

       points：二维码的角点，类型为 OutputArray。这个参数是一个输出参数，用于存储检测到的二维码的角点。如果检测成功，这个数组将包含四个点，每个点表示二维码的角点。

       straight_qrcode：输出图像，类型为 OutputArray。这个参数是一个可选的输出参数，用于存储解码后的二维码图像。如果不需要这个输出，可以传递 noArray()。

       返回值：

       返回一个 std::string，表示解码后的二维码内容。
*/

        if(cvui::button(frame, 940, 480, "Inbound"))//入库状态
        {
            IN_status = true;
            OUT_status = false;
            NO_status = false;
            cout << "INbound status now!" << endl;
        }

        if(cvui::button(frame, 1050, 480, "Outbound"))//出库状态
        {
            IN_status = false;
            OUT_status = true;
            NO_status = false;
            cout << "Outbound status now!" << endl;
        }

        // 文件输出流，使用 ofstream 打开文件
        ofstream all_num(filePath, ios::app); // 打开文件以追加模式写入,目的是将入库的二维码保存到文件中

        if (!all_num)
        {
            cerr << "Error opening file for writing at path: " << filePath << endl;
            return -1;
        }

        // 每隔一定帧数才进行 QR 码检测
        if(frameCount % detectInterval == 0)
        {
            // 检测摄像头中的二维码并进行识别
            decodedInfo = qrDecoder.detectAndDecode(frameCam, points , straight_qrcode);

            if(!straight_qrcode.empty())  // 判断是否有检测到二维码数据
            {
                // 直接绘制二维码的四条边，连接四个角点
                for (int i = 0; i < 4; i++)
                {
                    //cv::line: 这用于在图像上绘制一条线，points[i] 是线段的起始点，points[(i + 1) % 4] 是线段的终点
                    //(i + 1) % 4 确保循环连接四个点，最后一个点与第一个点相连，形成一个闭合的四边形
                    cv::line(frameCam, points[i], points[(i + 1) % 4], cv::Scalar(0, 255, 0), 4);
                }

                // 检查二维码是否已经在动态数组中
                auto it = find(infoArr.begin(), infoArr.end(), decodedInfo);


                //入库状态
                if(IN_status)
                {
                    if (it == infoArr.end())
                    {
                        // 如果不在，添加到数组和集合中
                        infoArr.push_back(decodedInfo);

                        inventory_quantity++;

                        string write_info = decodedInfo + "IN#" + "\r\n";//写入文件的信息

                        // 将入库信息写入文件
                        if (all_num.is_open())
                        {
                            all_num << write_info; // 写入到文件
                        }else
                        {
                            cerr << "Error opening file for writing!" << endl;
                        }

                        cout << "number: " <<decodedInfo << " IN" << endl ;
                    }
                    else
                    {
                        // 如果在,则不重复入库
                        cout << "The order number already exists" << endl;
                    }
                }

                //出库状态
                if(OUT_status)
                {
                    if (it != infoArr.end())
                    {
                        // 如果在，进行出库操作
                        infoArr.erase(it);

                        pick_up_vol++;

                        // 将出库信息写入文件
                        string write_info = decodedInfo + "OUT#" + "\r\n";

                        // 写入文件
                        all_num << write_info;

                        cout << "number: " << decodedInfo << " OUT" << endl;

                        cout << "Please pick up the package" << endl;
                    }
                    else
                    {
                        // 如果不在，提示包裹已被取走或者没有这包裹
                        cout << "The package has been picked up or The order number does not exist" << endl;
                    }
                }

                //默认无状态
                if(NO_status)
                {
                   cout << "Please click the button to confirm the current QR code scanning status" << endl;
                }
            }
        }

        //关闭文件
        all_num.close();


        frameCount++;  // 增加帧计数器

        //摄像头位置,放在这里显示是因为有二维码扫描的时候，二维码框要在摄像头上面显示，所以如果要显示二维码框，要将他底部框架放后面
        cvui::image(frame,40,40,frameCam);



        // 在窗口中创建一个输入框
        cvui::input(frame, 720, 480, 120,"Tracking number:", inputText);

        // 在窗口中创建一个按钮
        if (cvui::button(frame, 847, 480, "inquire"))
        {            
            // 进行订单号查询
            inquireOrderStatus(inputText);
        }


        //关机键
        int close_sign = 0;
        if (cvui::button(frame, 1230, 20, out, over, down))
        {
            close_sign = 1;
        }


        // 此函数必须在*AFTER*所有UI组件后调用
        // 它做了所有的幕后魔术来处理鼠标点击等
        cvui::update();

        // 在屏幕上显示所有内容
        cv::imshow(WINDOW_NAME, frame);

        // 检查是否按下了ESC键
        if (cv::waitKey(20) == 27 || close_sign)
        {
            break;
        }
    }

    // 释放摄像头并关闭窗口
    cap.release();
    cv::destroyAllWindows();
    return 0;
}
